#include "conversion.h"
#include "../../semantics/method.h"
#include "../../semantics/field.h"
#include "../../semantics/error.h"
#include "../../semantics/visitordata.h"
#include "../../semantics/classsemantic.h"
#include "../../code/callfactory.h"
#include "assert.h"
#include "classnode.h"


void ConversionNode::write(GraphStream * s)
{
 this->accept(s);
 fprintf(s->stream,"\tnode_%d [label=\"(%d,%d)\\nType conversion\" shape=\"rectangle\"]\n", this->number(),this->line(),this->position());
 m_type->write(s);
 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"to type\"]\n",this->number(),m_type->number());
 m_expr->write(s);
 fprintf(s->stream,"\tnode_%d -> node_%d [label=\"expression\"]\n",this->number(),m_expr->number());
}

ConversionNode::~ConversionNode()
{
	if (this->isOwningData())
	{
		delete m_type;
		delete m_expr;
		delete m_moveexpr;
	}
	delete m_delegate;
}

void ConversionNode::checkCommonErrors(ParentData * data)
{
	ClassSemantic * to=data->Classes->getClassByName(m_type);
	if (!to)
	{
		data->Classes->add(new ClassDoesNotExistsError(m_type,m_type->fullName()));
		return;
	}
	if (isVoidIdentifier(m_expr))
	{
		m_moveexpr=m_expr;
		m_expr=NULL;
		m_delegate=new NullReference();
		return;
	}

	std::string type=m_expr->getType(data);
	ClassSemantic * from=data->Classes->getClassByName(type);
	if (from==NULL)
	{
		return;
	}
	
	if (m_type->fullName()==type)
	{
		m_delegate=new DoNothing();
	}
	else
	{
		if (data->Classes->isDerived(m_type->fullName(),type))
		{
			m_delegate=new DowncastDelegate(from,to);
		}
		else
		{
			m_delegate=selectArgumentConversion(m_expr,m_type->fullName(),data,data->CurrentMethod);
		}
	}
}


void ConversionNode::fillConstant(ParentData * data)
{
	if (m_expr)
		m_expr->fillConstant(data);
	m_delegate->fillConstant(data);
}

void ConversionNode::produceCode(ParentData * data)
{
	if (m_expr)
		m_expr->produceCode(data);
	m_delegate->produceCode(data);
}
std::string ConversionNode::getType(ParentData * data)
{
	checkCommonErrors(data);
	return m_type->fullName();
}